/* Classic Ladder Project */
/* Copyright (C) 2001-2011 Marc Le Douarain */
/* http://membres.lycos.fr/mavati/classicladder/ */
/* http://www.sourceforge.net/projects/classicladder */
/* February 2001 */
/* ---------------------------------- */
/* GTK Interface & Main */
/* Inspired (at the start) from the scribble example. */
/* ---------------------------------- */
/* This library is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Lesser General Public */
/* License as published by the Free Software Foundation; either */
/* version 2.1 of the License, or (at your option) any later version. */

/* This library is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU */
/* Lesser General Public License for more details. */

/* You should have received a copy of the GNU Lesser General Public */
/* License along with this library; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <cairo.h>
#include <cairo-svg.h>
#include <zlib.h>

#include "classicladder.h"
#include "global.h"
#include "classicladder_gtk.h"
#include "hardware.h"
#ifdef SEQUENTIAL_SUPPORT
#include "drawing_sequential.h"
#endif
#include "log_events.h"
#include "menu_and_toolbar_gtk.h"
#include "preferences.h"
#include "search.h"
#include "monitor_windows_gtk.h"

//Cairo GdkPixmap *pixmap = NULL;
GtkWidget *drawing_area = NULL;
GtkWidget *entrylabel,*entrycomment;
GtkWidget *CheckDispSymbols;
//#if defined( RT_SUPPORT ) || defined( __XENO__ )
GtkWidget *WidgetDurationOfLastScan;
//#endif
//Since menu/toolbar... GtkWidget *ButtonRunStop;
GtkWidget *VScrollBar;
GtkWidget *HScrollBar;
GtkAdjustment * AdjustVScrollBar;
GtkAdjustment * AdjustHScrollBar;
GtkWidget *FileSelector;
GtkWidget *ConfirmDialog;
GtkWidget *MainSectionWindow;
GtkWidget *StatusBar;
gint StatusBarContextId;

GtkWidget *hBoxSearch;
GtkWidget *ButtonSearchClose;
GtkWidget *SearchEntry;
GtkWidget *SearchType;
GtkWidget *ButtonSearchNow;
GtkWidget *ButtonSearchNext;
GtkWidget *ButtonSearchPrev;

GtkWidget *hBoxFileTransfer;
GtkWidget *FileTransferBar;
GtkWidget *FileTransferLabel;
GtkWidget *FileTransferAbortButton;

#include "drawing.h"
#include "vars_access.h"
#include "calc.h"
#include "files_project.h"
#include "edit.h"
#include "edit_gtk.h"
#include "editproperties_gtk.h"
#include "manager_gtk.h"
#include "config_gtk.h"
#include "socket_server.h"
#include "socket_modbus_master.h"
#ifdef SEQUENTIAL_SUPPORT
#include "calc_sequential.h"
#endif
//#ifdef GNOME_PRINT_USE
//#include "print_gnome.h"
//#endif
#include "symbols_gtk.h"
#include "spy_vars_gtk.h"
#include "print_gtk.h"
#include "vars_system.h"
#include "log_events_gtk.h"
#include "tasks.h"

void CairoDrawCurrentSectionOnDrawingArea( void )
{
	cairo_t *cr = gdk_cairo_create( drawing_area->window );
	/* clean up */
	double w,h;
	w = drawing_area->allocation.width;
	h = drawing_area->allocation.height;
	cairo_set_source_rgb( cr, 1, 1 ,1 );
	cairo_rectangle( cr, 0.0, 0.0, w, h );
	cairo_fill( cr );
	GetTheSizesForRung( );
	DrawCurrentSection( cr );
	cairo_destroy( cr );
}

/* Create a new backing pixmap of the appropriate size */
/*static gint configure_event( GtkWidget		 *widget,
							GdkEventConfigure *event )
{
	if (pixmap)
		gdk_pixmap_unref(pixmap);

	pixmap = gdk_pixmap_new(widget->window,
							widget->allocation.width,
							widget->allocation.height,
							-1);
	gdk_draw_rectangle (pixmap,
						widget->style->white_gc,
						TRUE,
						0, 0,
						widget->allocation.width,
						widget->allocation.height);
	return TRUE;
}*/

/* Redraw the screen with Cairo */
static gint expose_event( GtkWidget	  *widget,
						GdkEventExpose *event )
{
/*	gdk_draw_pixmap(widget->window,
					widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
					pixmap,
					event->area.x, event->area.y,
					event->area.x, event->area.y,
					event->area.width, event->area.height);
*/
	CairoDrawCurrentSectionOnDrawingArea( );
	return FALSE;
}

void GetTheSizesForRung( void )
{
	static int PageHeightBak = 0;
	static int BlockHeightBak = 0;
	InfosGene->BlockWidth = ((GTK_WIDGET(drawing_area)->allocation.width*995/1000) / RUNG_WIDTH);
	// keep ratio aspect (if defaults values of size block not square)
	InfosGene->BlockHeight = InfosGene->BlockWidth*BLOCK_HEIGHT_DEF/BLOCK_WIDTH_DEF;

	InfosGene->PageHeight = GTK_WIDGET(drawing_area)->allocation.height;
	// used for sequential
	InfosGene->PageWidth = GTK_WIDGET(drawing_area)->allocation.width;

	// size of the page or block changed ?
	if ( InfosGene->PageHeight!=PageHeightBak || InfosGene->BlockHeight!=BlockHeightBak )
		UpdateVScrollBar( TRUE/*AutoSelectCurrentRung*/ );
	PageHeightBak = InfosGene->PageHeight;
	BlockHeightBak = InfosGene->BlockHeight;
}

// calc total nbr rungs in a section, and nbr of rungs before current rung.
void GetCurrentNumAndNbrRungsForASection( int * pCurrNumRung, int * pNbrRungs )
{
	int iSecurityBreak = 0;
	int NbrRungs = 1;
	int ScanRung = InfosGene->FirstRung;
	int NumCurrentRung = 0;
	while ( ScanRung!=InfosGene->LastRung && iSecurityBreak++<=NBR_RUNGS )
	{
		NbrRungs++;
		ScanRung = RungArray[ ScanRung ].NextRung;
	}
	ScanRung = InfosGene->FirstRung;
	iSecurityBreak = 0;
	while ( ScanRung!=InfosGene->CurrentRung && iSecurityBreak++<=NBR_RUNGS )
	{
		NumCurrentRung++;
		ScanRung = RungArray[ ScanRung ].NextRung;
	}
	if ( iSecurityBreak>=NBR_RUNGS )
		debug_printf("!!!error loop in %s()!\n",__FUNCTION__);
	if ( pCurrNumRung!=NULL )
		*pCurrNumRung = NumCurrentRung;
	if ( pNbrRungs!=NULL )
		*pNbrRungs = NbrRungs;
}
// flag used when signal event will be called on "value changed", to remember to not do the auto-select of the current rung.
// if current rung selected by software (search), and value adjusted by calling this function, so of course current rung is visible ! ;-)
char RememberNotToAutoSelectCurrentRung = FALSE;
void UpdateVScrollBar( char AutoSelectCurrentRung )
{
	int iCurrentLanguage = SectionArray[ InfosGene->CurrentSection ].Language;
	RememberNotToAutoSelectCurrentRung = !AutoSelectCurrentRung;
	if ( iCurrentLanguage==SECTION_IN_LADDER )
	{
		int NbrTotalRungs = 1;
		int NbrRungsBeforeCurrent = 0;
		GetCurrentNumAndNbrRungsForASection( &NbrRungsBeforeCurrent, &NbrTotalRungs );
//printf("=> in %s(); NbrTotalRungs=%d , NbrRungsBeforeCurrent=%d AutoSelectCurrentRung=%d\n", __FUNCTION__, NbrTotalRungs, NbrRungsBeforeCurrent, AutoSelectCurrentRung);
		AdjustVScrollBar->lower = 0;
		AdjustVScrollBar->upper = NbrTotalRungs * TOTAL_PX_RUNG_HEIGHT;
		AdjustVScrollBar->value = NbrRungsBeforeCurrent *  TOTAL_PX_RUNG_HEIGHT;
		// go up lift to take into account the fact that total height page is generally more than just one rung...
		while( AdjustVScrollBar->value+InfosGene->PageHeight > AdjustVScrollBar->upper )
		{
			AdjustVScrollBar->value = AdjustVScrollBar->value - TOTAL_PX_RUNG_HEIGHT;
		}
		AdjustVScrollBar->step_increment = InfosGene->BlockHeight;
		AdjustVScrollBar->page_increment = TOTAL_PX_RUNG_HEIGHT;
		AdjustVScrollBar->page_size = InfosGene->PageHeight;
		gtk_adjustment_changed( AdjustVScrollBar );
		gtk_adjustment_value_changed( AdjustVScrollBar );
		gtk_widget_hide( HScrollBar );
	}
#ifdef SEQUENTIAL_SUPPORT
	if ( iCurrentLanguage==SECTION_IN_SEQUENTIAL )
	{
		gtk_widget_show( HScrollBar );
		refresh_label_comment( );
		AdjustVScrollBar->lower = 0;
		AdjustVScrollBar->upper = SEQ_PAGE_HEIGHT * SEQ_SIZE_DEF;
		AdjustVScrollBar->value = 0;
		AdjustVScrollBar->step_increment = SEQ_SIZE_DEF;
		AdjustVScrollBar->page_increment = InfosGene->PageHeight;
		AdjustVScrollBar->page_size = InfosGene->PageHeight;
		gtk_adjustment_changed( AdjustVScrollBar );
		gtk_adjustment_value_changed( AdjustVScrollBar );
		AdjustHScrollBar->lower = 0;
		AdjustHScrollBar->upper = SEQ_PAGE_WIDTH * SEQ_SIZE_DEF;
		AdjustHScrollBar->value = 0;
		AdjustHScrollBar->step_increment = SEQ_SIZE_DEF;
		AdjustHScrollBar->page_increment = InfosGene->PageWidth;
		AdjustHScrollBar->page_size = InfosGene->PageWidth;
		gtk_adjustment_changed( AdjustHScrollBar );
		gtk_adjustment_value_changed( AdjustHScrollBar );
	}
#endif
}

// - used to auto-select current rung when lift moved
// - also used when user click on page to select its current rung (to edit later)
void ChoiceOfTheCurrentRung( int NbrOfRungsAfterTopRung )
{
	int DecptNbrRungs = NbrOfRungsAfterTopRung;

//printf("OffsetHiddenTopRungDisplayed=%d\n", InfosGene->OffsetHiddenTopRungDisplayed);
	// per default, the current rung, is the top one displayed...
	InfosGene->CurrentRung = InfosGene->TopRungDisplayed;

//printf("=> start choice of current rung, %d passes...(CurrentRung=%d)\n",NbrOfRungsAfterTopRung, InfosGene->CurrentRung);
	while( DecptNbrRungs>0 && InfosGene->CurrentRung!=InfosGene->LastRung )
	{
		InfosGene->CurrentRung = RungArray[ InfosGene->CurrentRung ].NextRung;
		DecptNbrRungs--;
	}
//printf("=> end choice of current rung. (CurrentRung=%d)\n", InfosGene->CurrentRung);
}
void CalcOffsetCurrentRungDisplayed( void )
{
	int ScanRung = InfosGene->TopRungDisplayed;
	int NbrOfRungsFnd = 0;
	// if OffsetHiddenTopRungDisplayed==0, vertical shift of the current rung is 0,
	// else this is the y value to substract to have the vertical shift... (we will add many full rungs heights after!)
	InfosGene->OffsetCurrentRungDisplayed = -1*InfosGene->OffsetHiddenTopRungDisplayed;
//	InfosGene->OffsetCurrentRungDisplayed += NbrOfRungsAfterTopRung*TOTAL_PX_RUNG_HEIGHT;
	while( ScanRung!=InfosGene->CurrentRung )
	{
		ScanRung = RungArray[ ScanRung ].NextRung;
		InfosGene->OffsetCurrentRungDisplayed += TOTAL_PX_RUNG_HEIGHT;
		NbrOfRungsFnd++;
	}
//printf("=> In %s, CurrentRung=%d , NbrOfRungsAfterTopRung=%d, OffsetCurrentRungDisplayed=%d\n", __FUNCTION__, InfosGene->CurrentRung, NbrOfRungsFnd, InfosGene->OffsetCurrentRungDisplayed);
//////test	if ( InfosGene->OffsetCurrentRungDisplayed<0 )
//////test		debug_printf( "Error in ChoiceOfTheCurrentRung( %d ) with OffsetCurrentRungDisplayed=%d\n", NbrOfRungsFnd, InfosGene->OffsetCurrentRungDisplayed );
	refresh_label_comment( );
}

static gint VScrollBar_value_changed_event( GtkAdjustment * ScrollBar, void * not_used )
{
	int iCurrentLanguage = SectionArray[ InfosGene->CurrentSection ].Language;
	if ( iCurrentLanguage==SECTION_IN_LADDER )
	{
		int NbrRungsBeforeCurrent = ((int)ScrollBar->value)/TOTAL_PX_RUNG_HEIGHT;
		int ScanRung = 0;
		InfosGene->TopRungDisplayed = InfosGene->FirstRung;
		if ( NbrRungsBeforeCurrent<0 )
			NbrRungsBeforeCurrent = 0;
		while( ScanRung!=NbrRungsBeforeCurrent )
		{
			InfosGene->TopRungDisplayed = RungArray[ InfosGene->TopRungDisplayed ].NextRung;
			ScanRung++;
		}
		InfosGene->OffsetHiddenTopRungDisplayed = ((int)ScrollBar->value)%TOTAL_PX_RUNG_HEIGHT;
//printf("=> VScrollBar 'value changed' call ; NbrRungsBeforeCurrent=%d OffsetHiddenTopRungDisplayed=%d TopRungDisplayed=%d DoNotAutoSelectRung=%d\n",NbrRungsBeforeCurrent,InfosGene->OffsetHiddenTopRungDisplayed,InfosGene->TopRungDisplayed,RememberNotToAutoSelectCurrentRung);

		if ( RememberNotToAutoSelectCurrentRung==FALSE )
		{
			// auto select current rung which is the first completely displayed on the page.
			// if top rung displayed entirely (no vertical offset), it's the current rung => give '0'.
			// else, search the next one => give '1'.
			ChoiceOfTheCurrentRung( (InfosGene->OffsetHiddenTopRungDisplayed>0)?1:0 );
		}
		RememberNotToAutoSelectCurrentRung = FALSE;
		CalcOffsetCurrentRungDisplayed( );
	}
	InfosGene->VScrollValue = (int)ScrollBar->value;
	return TRUE;
}
static gint HScrollBar_value_changed_event( GtkAdjustment * ScrollBar, void * not_used )
{
	InfosGene->HScrollValue = (int)ScrollBar->value;
	return TRUE;
}

static gboolean button_press_event( GtkWidget *widget, GdkEventButton *event )
{
	if (event->button == 1 /*Cairo && pixmap != NULL*/)
	{
		if (EditDatas.ModeEdit)
		{
			EditElementInThePage(event->x,event->y);
		}
		else
		{
			// we can select the current rung by clicking on one.
			// the current rung is the one that will be modified...
			int iCurrentLanguage = SectionArray[ InfosGene->CurrentSection ].Language;
			if ( iCurrentLanguage==SECTION_IN_LADDER )
			{
				char DoSelection = TRUE;
				if ( InfosGene->OffsetHiddenTopRungDisplayed>0 )
				{
//////test					if ( event->y<TOTAL_PX_RUNG_HEIGHT-InfosGene->OffsetHiddenTopRungDisplayed )
//////test						DoSelection = FALSE;
				}
				if ( DoSelection )
				{
					int NbrRungsShift =  (event->y+InfosGene->OffsetHiddenTopRungDisplayed)/TOTAL_PX_RUNG_HEIGHT;
//printf("=> Select the current rung clicked, with a shift of rungs=%d\n", NbrRungsShift );
					ChoiceOfTheCurrentRung( NbrRungsShift );
					CalcOffsetCurrentRungDisplayed( );
//DisplayedNowDirectlyOnMotion					MessageInStatusBar( GetLadderElePropertiesForStatusBar( event->x,event->y ) );
				}
			}
		}
	}
	return TRUE;
}
static gboolean motion_notify_event( GtkWidget *widget, GdkEventMotion *event, gpointer user_data )
{
	if (EditDatas.ModeEdit)
	{
		MouseMotionOnThePage( event->x, event->y );
	}
	else
	{
		if ( SectionArray[ InfosGene->CurrentSection ].Language==SECTION_IN_LADDER )
			MessageInStatusBar( GetLadderElePropertiesForStatusBar( event->x,event->y ) );
	}
	return TRUE;
}
static gboolean button_release_event( GtkWidget *widget, GdkEventButton *event )
{
	if (event->button == 1 /*Cairo && pixmap != NULL*/)
	{
		if (EditDatas.ModeEdit)
			EditButtonReleaseEventOnThePage( );
	}
	return TRUE;
}
void refresh_label_comment( void )
{
	StrRung * RfhRung;
	if ( SectionArray[ InfosGene->CurrentSection ].Language==SECTION_IN_LADDER )
	{
		RfhRung = &RungArray[InfosGene->CurrentRung];
		gtk_entry_set_text((GtkEntry *)entrylabel,RfhRung->Label);
		gtk_entry_set_text((GtkEntry *)entrycomment,RfhRung->Comment);
	}
	else
	{
		gtk_entry_set_text((GtkEntry *)entrylabel,"");
		gtk_entry_set_text((GtkEntry *)entrycomment,"");
	}
}
void clear_label_comment()
{
	gtk_entry_set_text((GtkEntry *)entrylabel,"");
	gtk_entry_set_text((GtkEntry *)entrycomment,"");
}

void save_label_comment_edited()
{
	strcpy(EditDatas.Rung.Label,gtk_entry_get_text((GtkEntry *)entrylabel));
	strcpy(EditDatas.Rung.Comment,gtk_entry_get_text((GtkEntry *)entrycomment));
}

void autorize_prevnext_buttons(int Yes)
{
	if (Yes)
	{
		gtk_widget_set_sensitive(VScrollBar, TRUE);
		gtk_widget_set_sensitive(entrylabel, FALSE);
		gtk_widget_set_sensitive(entrycomment, FALSE);
	}
	else
	{
		gtk_widget_set_sensitive(VScrollBar, FALSE);
		gtk_widget_set_sensitive(entrylabel, TRUE);
		gtk_widget_set_sensitive(entrycomment, TRUE);
	}
}

void CheckDispSymbols_toggled( )
{
	InfosGene->DisplaySymbols = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( CheckDispSymbols ) );
}


void StoreDirectorySelected( GtkFileChooser *selector, char cForLoadingProject)
{
	char * TempDir;

	TempDir = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(FileSelector));
	if ( cForLoadingProject )
		VerifyDirectorySelected( InfosGene->CurrentProjectFileName, TempDir );
	else
		strcpy( InfosGene->CurrentProjectFileName, TempDir );
}


void LoadNewLadder()
{
	char ProjectLoadedOk;
	StoreDirectorySelected( GTK_FILE_CHOOSER(FileSelector) , TRUE/*cForLoadingProject*/);
	
	if (InfosGene->LadderState==STATE_RUN)
	{
		DoFlipFlopRunStop( );
	}
	InfosGene->LadderState = STATE_LOADING;
	ProjectLoadedOk = LoadProjectFiles( InfosGene->CurrentProjectFileName );
	if ( !ProjectLoadedOk )
		ShowMessageBox( "Load Error", "Failed to load the project file...", "Ok" );

	UpdateAllGtkWindows( );
	UpdateWindowTitleWithProjectName( );
	MessageInStatusBar( ProjectLoadedOk?"Project loaded (stopped).":"Project failed to load...");
#ifndef RT_SUPPORT
		OpenHardware( 0 );
//		ConfigHardware( );
	InfosGene->AskToConfHardInputs = TRUE;
	InfosGene->AskToConfHardOutputs = TRUE;
#endif
	InfosGene->LadderState = STATE_STOP;
//added here in 0.9.10, really right place?
ConfigSerialModbusMaster( );
}
void DoActionSave()
{
	if ( InfosGene->CurrentProjectFileName[0]=='\0' )
	{
		DoActionSaveAs( );
	}
	else
	{
		if ( InfosGene->AskConfirmationToQuit )
		{
			char BuffCurrTime[ 30 ];
			GetCurrentAscTime( BuffCurrTime );
			strcpy( InfosGene->ProjectProperties.ParamModifDate, BuffCurrTime );
			if ( InfosGene->ProjectProperties.ParamVersion[ 0 ]>='0' && InfosGene->ProjectProperties.ParamVersion[ 0 ]<='9' )
			{
				int version = 0;
				version = atoi( InfosGene->ProjectProperties.ParamVersion );
				version++;
				sprintf( InfosGene->ProjectProperties.ParamVersion, "%d", version );
			}
		}
		if ( !SaveProjectFiles( InfosGene->CurrentProjectFileName ) )
		{
			ShowMessageBox( "Save Error", "Failed to save the project file...", "Ok" );
		}
		else
		{
			UpdateWindowTitleWithProjectName( );
			DisplayProjectProperties( );
		}
	}
}

void SaveAsLadder(void)
{
	StoreDirectorySelected( GTK_FILE_CHOOSER(FileSelector), FALSE/*cForLoadingProject*/);
	DoActionSave( );
//	if ( !SaveProjectFiles( InfosGene->CurrentProjectFileName ) )
//		ShowMessageBox( "Save Error", "Failed to save the project file...", "Ok" );
//	UpdateWindowTitleWithProjectName( );
}

void
on_filechooserdialog_save_response(GtkDialog  *dialog,gint response_id,gpointer user_data)
{
	debug_printf("SAVE %s %d\n",gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(FileSelector)),response_id);	

	if(response_id==GTK_RESPONSE_ACCEPT || response_id==GTK_RESPONSE_OK)
		SaveAsLadder();
	gtk_widget_destroy(GTK_WIDGET(dialog));
}
void
on_filechooserdialog_load_response(GtkDialog  *dialog,gint response_id,gpointer user_data)
{
	debug_printf("LOAD %s %d\n",gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(FileSelector)),response_id);

	if(response_id==GTK_RESPONSE_ACCEPT || response_id==GTK_RESPONSE_OK)
		LoadNewLadder();
	gtk_widget_destroy(GTK_WIDGET(dialog));
}


void CreateFileSelection(char * Prompt,int Save)
{
	/* Create the selector */
	GtkFileFilter *FilterOldProjects, *FilterProjects;
	if(Save)
	{
		FileSelector = gtk_file_chooser_dialog_new (Prompt, NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
		gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(FileSelector), TRUE );
	}
	else
	{
		FileSelector = gtk_file_chooser_dialog_new (Prompt, NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
	}
	gtk_window_set_type_hint (GTK_WINDOW (FileSelector), GDK_WINDOW_TYPE_HINT_DIALOG);

	FilterOldProjects = gtk_file_filter_new( );
	gtk_file_filter_set_name( FilterOldProjects, "Old directories projects" );
	gtk_file_filter_add_pattern( FilterOldProjects, "*.csv" ); // old dir projects
	gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(FileSelector), FilterOldProjects );
	FilterProjects = gtk_file_filter_new( );
	gtk_file_filter_set_name( FilterProjects, "ClassicLadder projects" );
	gtk_file_filter_add_pattern( FilterProjects, "*.clprj" );
	gtk_file_filter_add_pattern( FilterProjects, "*.clp" );
	gtk_file_filter_add_pattern( FilterProjects, "*.clprjz" );
	gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(FileSelector), FilterProjects );
	gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(FileSelector), FilterProjects );

	gtk_window_set_modal(GTK_WINDOW(FileSelector), TRUE );

/*
  g_signal_connect ((gpointer) filechooserdialog, "file_activated",
					G_CALLBACK (on_filechooserdialog_file_activated),
					NULL);
					*/

	if(Save)
		g_signal_connect ((gpointer) FileSelector, "response",
					G_CALLBACK (on_filechooserdialog_save_response),
					NULL);
	else
		g_signal_connect ((gpointer) FileSelector, "response",
					G_CALLBACK (on_filechooserdialog_load_response),
					NULL);

	g_signal_connect_swapped ((gpointer) FileSelector, "close",
							G_CALLBACK (gtk_widget_destroy),
							GTK_OBJECT (FileSelector));

	/* Display that dialog */
	gtk_widget_show (FileSelector);
}

void DoNewProject( void )
{
	ClassicLadder_InitAllDatas( );
	UpdateAllGtkWindows( );
	InfosGene->CurrentProjectFileName[0] = '\0'; // no current project name
	UpdateWindowTitleWithProjectName( );
	InfosGene->AskConfirmationToQuit = TRUE;
	InfosGene->HasBeenModifiedForExitCode = TRUE;
}

void DoActionConfirmNewProject()
{
	ShowConfirmationBox("New","Do you really want to clear all datas ?",DoNewProject);
}
void DoLoadProject()
{
	CreateFileSelection("Please select the project to load",FALSE);
}

void DoActionLoadProject()
{
	if ( InfosGene->AskConfirmationToQuit )
		ShowConfirmationBox( "Sure?", "Do you really want to load another project ?\nIf not saved, all modifications on the current project will be lost  \n", DoLoadProject );
	else
		DoLoadProject( );
}

void DoActionSaveAs()
{
	CreateFileSelection("Please select the project to save",TRUE);
}

void DoActionResetAndConfirmIfRunning( )
{
	if ( (InfosGene->LadderState==STATE_RUN && !InfosGUI->TargetMonitor.RemoteConnected ) || ( InfosGUI->TargetMonitor.LatestTargetState==STATE_RUN && InfosGUI->TargetMonitor.RemoteConnected ) )
		ShowConfirmationBox("Warning!","Resetting a running program\ncan cause unexpected behavior\n Do you really want to reset?",DoReset);
	else
		DoReset();
}
//void ButtonConfig_click()
//{
//	OpenConfigWindowGtk( );
//}

void DoActionAboutClassicLadder()
{
	/*
	// From the example in gtkdialog help
	GtkWidget *dialog, *label, *okay_button;
	// Create the widgets
	dialog = gtk_dialog_new();
	label = gtk_label_new ( CL_PRODUCT_NAME " v" CL_RELEASE_VER_STRING "\n" CL_RELEASE_DATE_STRING "\n"
						"Copyright (C) " CL_RELEASE_COPYRIGHT_YEARS " Marc Le Douarain\nmarc . le - douarain /At\\ laposte \\DoT/ net\n"
						"http://www.sourceforge.net/projects/classicladder\n"
						"http://membres.lycos.fr/mavati/classicladder\n"
						"Released under the terms of the\nGNU Lesser General Public License v3");
	gtk_label_set_justify( GTK_LABEL(label), GTK_JUSTIFY_CENTER );
	okay_button = gtk_button_new_with_label("Okay");
	// Ensure that the dialog box is destroyed when the user clicks ok.
	gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
							GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(dialog));
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
					okay_button);
	gtk_widget_grab_focus(okay_button);
	// Add the label, and show everything we've added to the dialog.
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
					label);
	gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
	gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_CENTER);
	gtk_widget_show_all (dialog);
*/
	char * static_comments = "Released under the terms of the\nGNU Lesser General Public License v3\n\n"
							"Written by Marc Le Douarain\n"
							"and including contributions made by Chris Morley, Heli Tejedor, Bernard Chardonneau (base64 transfer) and others\n\n"
							"Latest software version available at:\nhttp://www.sourceforge.net/projects/classicladder\n"
							"Original site:\nhttp://membres.lycos.fr/mavati/classicladder\n";
	char * comments = malloc( strlen( static_comments )+80 );
	if ( comments )
	{
		char GtkVersionString[ 30 ];
		sprintf( GtkVersionString, "GTK+ version %d.%d.%d\n\n", gtk_major_version, gtk_minor_version, gtk_micro_version );
		strcpy( comments, "("CL_RELEASE_DATE_STRING")\n\n" );
		strcat( comments, GtkVersionString );
		strcat( comments, static_comments );
		gtk_show_about_dialog ( GTK_WINDOW( MainSectionWindow ),
							"program-name", CL_PRODUCT_NAME ,
							"version", CL_RELEASE_VER_STRING ,
							"copyright", "Copyright (C) " CL_RELEASE_COPYRIGHT_YEARS " Marc Le Douarain\nmarc . le - douarain /At\\ laposte \\DoT/ net" ,
//							"logo", example_logo,
							"title", "About ClassicLadder",
							"website", "http://sites.google.com/site/classicladder" ,
							"comments", comments ,
					   NULL );
		free( comments );
	}
}


cairo_surface_t *ExportSurface;
cairo_t * InitExportSurface( int SurfaceWidth, int SurfaceHeight, char * SvgFileToCreate )
{
	cairo_t *cr;
	if ( SvgFileToCreate )
	{
		ExportSurface = cairo_svg_surface_create( SvgFileToCreate, SurfaceWidth, SurfaceHeight );
		cr = cairo_create( ExportSurface );
	}
	else
	{
		ExportSurface = cairo_image_surface_create( CAIRO_FORMAT_RGB24, SurfaceWidth, SurfaceHeight );
		cr = cairo_create( ExportSurface );
		//cleanup
		cairo_set_source_rgb( cr, 1.0, 1.0, 1.0 );
		cairo_paint( cr );
	}
	return cr;	
}

void ExportSvgOrPngFile( char * FileToCreate, char GoForSvgExport )
{
	cairo_t *cr;
	int iCurrentLanguage = SectionArray[ InfosGene->CurrentSection ].Language;
	if ( iCurrentLanguage==SECTION_IN_LADDER )
	{
		int SvgWidthTotal = RUNG_WIDTH*BLOCK_WIDTH_DEF+30;
		int SvgHeightTotal = RUNG_HEIGHT*BLOCK_HEIGHT_DEF+BLOCK_HEIGHT_DEF/2;
		int LeftRightBarsWidth = BLOCK_WIDTH_DEF/16;
		cr = InitExportSurface( SvgWidthTotal, SvgHeightTotal, GoForSvgExport?FileToCreate:NULL );
		DrawLeftRightBars( cr, 0, 0, BLOCK_WIDTH_DEF, BLOCK_HEIGHT_DEF, BLOCK_HEIGHT_DEF/2, LeftRightBarsWidth, FALSE );
		DrawRung( cr, InfosGene->CurrentRung, FALSE/*InEdit*/, LeftRightBarsWidth, 0, BLOCK_WIDTH_DEF, BLOCK_HEIGHT_DEF, BLOCK_HEIGHT_DEF/2, DRAW_FOR_PRINT );
	}
	if ( iCurrentLanguage==SECTION_IN_SEQUENTIAL )
	{
		int SvgWidthTotal = SEQ_PAGE_WIDTH*SEQ_SIZE_DEF;
		int SvgHeightTotal = SEQ_PAGE_HEIGHT*SEQ_SIZE_DEF;
		cr = InitExportSurface(  SvgWidthTotal, SvgHeightTotal, GoForSvgExport?FileToCreate:NULL );
		DrawSequentialPage( cr, SectionArray[ InfosGene->CurrentSection ].SequentialPage, SEQ_SIZE_DEF, DRAW_FOR_PRINT );
	}
	if ( !GoForSvgExport )
		cairo_surface_write_to_png( ExportSurface, FileToCreate );
	cairo_surface_destroy( ExportSurface );
	cairo_destroy( cr );
}
void FileRequestToExportSvgOrPng(char GoForSvg)
{
	GtkWidget *dialog;
	dialog = gtk_file_chooser_dialog_new (GoForSvg?"Save SVG File":"Save PNG File",
										GTK_WINDOW(MainSectionWindow),
										GTK_FILE_CHOOSER_ACTION_SAVE,
										GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
										GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
										NULL);
	gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER (dialog), TRUE );
	gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER (dialog), GoForSvg?"classicladder_export.svg":"classicladder_export.png" );
	if ( gtk_dialog_run( GTK_DIALOG (dialog) ) == GTK_RESPONSE_ACCEPT )
	{
		char *filename;
		filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
		ExportSvgOrPngFile( filename, GoForSvg );
		g_free( filename );
	}
	gtk_widget_destroy( dialog );
}
void DoActionExportSvg( void )
{
	FileRequestToExportSvgOrPng( TRUE );
}
void DoActionExportPng( void )
{
	FileRequestToExportSvgOrPng( FALSE );
}
// use a temp .png file to make it... (Cairo Surface -> png file -> pixbuf -> clipboard)
void DoActionCopyToClipboard( void )
{
	GError * err = NULL;
	GtkClipboard * pClipboard = gtk_clipboard_get( GDK_SELECTION_CLIPBOARD );
	ExportSvgOrPngFile( "cl_clipboard_tmp.png", FALSE/*GoForSvgExport*/ );
printf("Creating gdk pixpuf from tmp png file\n");
	GdkPixbuf * pPixBuf = gdk_pixbuf_new_from_file( "cl_clipboard_tmp.png", &err );
	if ( pPixBuf )
	{
		remove( "cl_clipboard_tmp.png" );
printf("Set pixbuf image to clipboard\n");
		gtk_clipboard_set_image( pClipboard, pPixBuf );
		g_object_unref( pPixBuf );
	}
	else
	{
		printf("Error clipboard_set_image() : %s\n", err->message);
	}
}


void ShowMessageBox(const char * title, const char * text, const char * button)
{
	/* From the example in gtkdialog help */
	GtkWidget *dialog, *label, *okay_button;
	/* Create the widgets */
	dialog = gtk_dialog_new();
	label = gtk_label_new (text);
	okay_button = gtk_button_new_with_label(button);
	/* Ensure that the dialog box is destroyed when the user clicks ok. */
	gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
							GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(dialog));
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
					okay_button);
	gtk_widget_grab_focus(okay_button);
	/* Add the label, and show everything we've added to the dialog. */
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
					label);
	gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
	gtk_window_set_title(GTK_WINDOW(dialog),title);
	gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_CENTER);
	gtk_widget_show_all (dialog);
}

void DoFunctionOfConfirmationBox(void * (*function_to_do)(void *))
{
	gtk_widget_destroy(ConfirmDialog);
	(function_to_do)(NULL);
}
void ShowConfirmationBoxWithChoiceOrNot(const char * title,const char * text,void * function_if_yes, char HaveTheChoice)
{
	/* From the example in gtkdialog help */
	GtkWidget *label, *yes_button, *no_button;
	/* Create the widgets */
	ConfirmDialog = gtk_dialog_new();
	label = gtk_label_new (text);
	if ( HaveTheChoice )
	{
		yes_button = gtk_button_new_with_label("Yes");
		no_button = gtk_button_new_with_label("No");
	}
	else
	{
		yes_button = gtk_button_new_with_label("Ok");
	}
	/* Ensure that the dialog box is destroyed when the user clicks ok. */
	if ( HaveTheChoice )
	{
		gtk_signal_connect_object (GTK_OBJECT (no_button), "clicked",
							GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(ConfirmDialog));
		gtk_container_add (GTK_CONTAINER (GTK_DIALOG(ConfirmDialog)->action_area),
					no_button);
		gtk_widget_grab_focus(no_button);
	}
	gtk_signal_connect_object (GTK_OBJECT (yes_button), "clicked",
							GTK_SIGNAL_FUNC (DoFunctionOfConfirmationBox), function_if_yes);
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(ConfirmDialog)->action_area),
					yes_button);
	/* Add the label, and show everything we've added to the dialog. */
	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(ConfirmDialog)->vbox),
					label);
	gtk_window_set_modal(GTK_WINDOW(ConfirmDialog),TRUE);
	gtk_window_set_title(GTK_WINDOW(ConfirmDialog),title);
	gtk_window_set_position(GTK_WINDOW(ConfirmDialog),GTK_WIN_POS_CENTER);
	gtk_widget_show_all (ConfirmDialog);
}
void ShowConfirmationBox(const char * title,const char * text,void * function_if_yes)
{
	ShowConfirmationBoxWithChoiceOrNot( title, text, function_if_yes, TRUE );
}

char ShowEnterMessageBox( const char * title, const char * text, char * StoreStringResult )
{
	GtkWidget* pDialogBox;
	GtkWidget* pEntry;
	char OkDone = FALSE;

	pDialogBox = gtk_dialog_new_with_buttons(title,
		GTK_WINDOW( MainSectionWindow ),
		GTK_DIALOG_MODAL,
		GTK_STOCK_OK,GTK_RESPONSE_OK,
		GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,
		NULL);
	
	if ( text )
	{
		GtkWidget * pLabel = gtk_label_new( text );
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pDialogBox)->vbox), pLabel, TRUE, FALSE, 0);
	}
	pEntry = gtk_entry_new();
	gtk_entry_set_text( GTK_ENTRY(pEntry), StoreStringResult );
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pDialogBox)->vbox), pEntry, TRUE, FALSE, 0);
	gtk_widget_show_all(GTK_DIALOG(pDialogBox)->vbox);

	switch (gtk_dialog_run(GTK_DIALOG(pDialogBox)))
	{
		case GTK_RESPONSE_OK:
			strcpy( StoreStringResult, gtk_entry_get_text(GTK_ENTRY(pEntry) ) );
			OkDone = TRUE;
			break;
	}

	gtk_widget_destroy(pDialogBox);
	return OkDone;
}

void QuitAppliGtk()
{
	// look all window open/closed states + posis/sizes if opened (to restore them at next startup !)
RememberManagerWindowPrefs( );//not open-close state for this window, see TODO note in function...
	RememberEditWindowPrefs( );
	RememberSymbolsWindowPrefs( );
	RememberBoolVarsWindowPrefs( );
	RememberFreeVarsWindowPrefs( );
	RememberLogBookWindowPrefs( );
	RememberMonitorWindowPrefs( );
	
	ClassicLadderEndOfAppli( );
	gtk_exit( InfosGene->HasBeenModifiedForExitCode?1:0 );
}

void DoQuitGtkApplication( void )
{
	RememberWindowPosiPrefs( "Main", MainSectionWindow, TRUE/*SaveWindowSize*/ );
	gtk_widget_destroy( MainSectionWindow ); //sends signal "destroy" that will call QuitAppliGtk()...
}
void ConfirmQuit( void )
{
	if ( InfosGene->AskConfirmationToQuit )
		ShowConfirmationBox( "Warning!", "If not saved, all modifications will be lost.\nDo you really want to quit ?\n", DoQuitGtkApplication );
	else if (EditDatas.ModeEdit==TRUE )
		ShowConfirmationBox( "Warning!", "You are curently under edit.\nDo you really want to quit ?\n", DoQuitGtkApplication );
	else
		DoQuitGtkApplication( );
}
gint MainSectionWindowDeleteEvent( GtkWidget * widget, GdkEvent * event, gpointer data )
{
	ConfirmQuit( );
	// we do not want that the window be destroyed.
	return TRUE;
}

void MessageInStatusBar( char * msg )
{
  gtk_statusbar_pop(GTK_STATUSBAR(StatusBar), StatusBarContextId);
  gtk_statusbar_push(GTK_STATUSBAR(StatusBar), StatusBarContextId, msg);
}

/* =========================================================================*/
/* GTK SEARCH BOX ... */
/* =========================================================================*/
extern char *SearchTypesNames[NBR_SEARCH_TYPES];
void FunctionNewSearchData( void )
{
	char * VarOrNbr;
	int Idx;
	VarOrNbr = (char *)gtk_entry_get_text(GTK_ENTRY(SearchEntry));
	Idx = gtk_combo_box_get_active (GTK_COMBO_BOX(SearchType));
	SearchNewElement( Idx, VarOrNbr );
}
void FunctionSearchCloseBox( void )
{
	gtk_widget_hide(hBoxSearch);
	SearchDatas.ElementOrSearchType = ELE_FREE;
}
void FunctionSearchOpenBox( void )
{
	gtk_widget_show(hBoxSearch);
	gtk_widget_grab_focus( SearchEntry );
}
void SearchInitGtk(GtkBox *vbox)
{
	int Scan;
	GtkTooltips * SearchEntryTooltip;

	hBoxSearch = gtk_hbox_new (FALSE,0);
	gtk_container_add (GTK_CONTAINER (vbox), hBoxSearch);
	gtk_box_set_child_packing(GTK_BOX(vbox), hBoxSearch,
		/*expand*/ FALSE, /*fill*/ FALSE, /*pad*/ 0, GTK_PACK_START);

	ButtonSearchClose = gtk_button_new();
	gtk_button_set_image( GTK_BUTTON( ButtonSearchClose ), gtk_image_new_from_stock( GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON ) );
	gtk_box_pack_start( GTK_BOX (hBoxSearch), ButtonSearchClose, FALSE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT (ButtonSearchClose), "clicked", (GtkSignalFunc) FunctionSearchCloseBox, 0 );
	gtk_widget_show( ButtonSearchClose );

	SearchEntry = gtk_entry_new();
//	gtk_widget_set_usize((GtkWidget *)SearchEntry,65,0);
	gtk_box_pack_start (GTK_BOX (hBoxSearch), SearchEntry, FALSE, FALSE, 4);
	gtk_widget_show (SearchEntry);
	gtk_signal_connect(GTK_OBJECT (SearchEntry), "activate", (GtkSignalFunc) FunctionNewSearchData, NULL);
	SearchEntryTooltip = gtk_tooltips_new();
	gtk_tooltips_set_tip( SearchEntryTooltip, SearchEntry, "Variable to search or block number", NULL );

	SearchType = gtk_combo_box_new_text();
	for( Scan=0; Scan<NBR_SEARCH_TYPES; Scan++ )
		gtk_combo_box_append_text( GTK_COMBO_BOX(SearchType), SearchTypesNames[Scan] );
	gtk_box_pack_start(GTK_BOX (hBoxSearch), SearchType, FALSE, FALSE, 0);
	gtk_combo_box_set_active (GTK_COMBO_BOX(SearchType), 0);
	gtk_widget_show (SearchType);
	gtk_signal_connect(GTK_OBJECT (SearchType), "changed", (GtkSignalFunc) FunctionNewSearchData, NULL);

	ButtonSearchNow = gtk_button_new();
	gtk_button_set_image( GTK_BUTTON( ButtonSearchNow ), gtk_image_new_from_stock( GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON ) );
	gtk_box_pack_start (GTK_BOX (hBoxSearch), ButtonSearchNow, FALSE, FALSE, 0);
	gtk_signal_connect(GTK_OBJECT (ButtonSearchNow), "clicked", (GtkSignalFunc) FunctionNewSearchData, 0);
	gtk_widget_set_sensitive( ButtonSearchNow, TRUE );
	gtk_widget_show( ButtonSearchNow );

	ButtonSearchNext = gtk_button_new();
	gtk_button_set_image( GTK_BUTTON( ButtonSearchNext ), gtk_image_new_from_stock( GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON ) );
	gtk_box_pack_start( GTK_BOX (hBoxSearch), ButtonSearchNext, FALSE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT (ButtonSearchNext), "clicked", (GtkSignalFunc) SearchAndGoToNextElement, 0 );
	gtk_widget_show( ButtonSearchNext );
	ButtonSearchPrev = gtk_button_new();
	gtk_button_set_image( GTK_BUTTON( ButtonSearchPrev ), gtk_image_new_from_stock( GTK_STOCK_GO_BACK, GTK_ICON_SIZE_BUTTON ) );
	gtk_box_pack_start( GTK_BOX (hBoxSearch), ButtonSearchPrev, FALSE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT (ButtonSearchPrev), "clicked", (GtkSignalFunc) SearchAndGoToPreviousElement, 0 );
	gtk_widget_show( ButtonSearchPrev );
}

/* =========================================================================*/
/* GTK FILE TRANSFER BOX ... */
/* =========================================================================*/
void FileTransferUpdateInfosGtk( void )
{
	if ( InfosGUI->TargetMonitor.RemoteFileTransfer )
	{
		char Buff[ 20 ];
		double BarValue = 0;
		StrFileTransfer * pFileTrans = GetPtrFileTransForMaster( );
		gtk_widget_show( hBoxFileTransfer );
		sprintf( Buff, "File%d%sX", InfosGUI->TargetMonitor.TransferFileNum, InfosGUI->TargetMonitor.TransferFileIsSend?"->":"<-");
		gtk_label_set_text( GTK_LABEL(FileTransferLabel), Buff );
		if ( pFileTrans->NumBlock>=0 )
		{
			if ( pFileTrans->NbrTotalBlocks>0 )
				BarValue = (double)pFileTrans->NumBlock/(double)(pFileTrans->NbrTotalBlocks+1.0);
printf("calc progress bar (file transfer), num=%d, nbr=%d, calc=%f\n", pFileTrans->NumBlock, pFileTrans->NbrTotalBlocks, BarValue );
			if ( BarValue>=1.0 )
				BarValue = 1.0;
			gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR(FileTransferBar), BarValue );
		}
	}
	else
	{
printf("ask to hide progress bar...\n");
		gtk_widget_hide( hBoxFileTransfer );
	}
}
void FileTransferInitGtk(GtkBox *vbox)
{
	hBoxFileTransfer = gtk_hbox_new (FALSE,0);
	gtk_container_add (GTK_CONTAINER (vbox), hBoxFileTransfer);
	gtk_box_set_child_packing(GTK_BOX(vbox), hBoxFileTransfer,
		/*expand*/ FALSE, /*fill*/ FALSE, /*pad*/ 0, GTK_PACK_START);

	FileTransferLabel = gtk_label_new( "-" );
	gtk_box_pack_start (GTK_BOX (hBoxFileTransfer), FileTransferLabel, FALSE, FALSE, 4);
	gtk_widget_show( FileTransferLabel );

	FileTransferBar = gtk_progress_bar_new( );
	gtk_box_pack_start (GTK_BOX (hBoxFileTransfer), FileTransferBar, FALSE, FALSE, 4);
	gtk_widget_show( FileTransferBar );
	
	FileTransferAbortButton = gtk_button_new();
	gtk_button_set_image( GTK_BUTTON( FileTransferAbortButton ), gtk_image_new_from_stock( GTK_STOCK_STOP, GTK_ICON_SIZE_BUTTON ) );
	gtk_box_pack_start( GTK_BOX (hBoxFileTransfer), FileTransferAbortButton, FALSE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT (FileTransferAbortButton), "clicked", (GtkSignalFunc) TransferMasterAskToAbort, 0 );
	gtk_widget_show( FileTransferAbortButton );
}

void MainSectionWindowInitGtk()
{
	GtkWidget *vbox,*hboxtop; //,*hboxbottom,*hboxbottom2;
	GtkWidget *hboxmiddle;
//	GtkWidget *ButtonQuit;
//	GtkWidget *ButtonNew,*ButtonLoad,*ButtonSave,*ButtonSaveAs,*ButtonReset,*ButtonConfig,*ButtonAbout;
//	GtkWidget *ButtonEdit,*ButtonSymbols,*ButtonSpyVars,*ButtonLogBook;
//#ifdef GNOME_PRINT_USE
//	GtkWidget *ButtonPrint,*ButtonPrintPreview,*ButtonExportSVG,*ButtonExportPNG,*ButtonCopyToClipboard;
//#endif
	GtkTooltips * TooltipsEntryLabel, * TooltipsEntryComment;
	GtkUIManager * PtrUIManager;

	MainSectionWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title ( GTK_WINDOW(MainSectionWindow), "ClassicLadder Section Display");
RestoreWindowPosiPrefs( "Main", MainSectionWindow );

	vbox = gtk_vbox_new (FALSE, 0);
	gtk_container_add (GTK_CONTAINER (MainSectionWindow), vbox);
	gtk_widget_show (vbox);

	gtk_signal_connect (GTK_OBJECT (MainSectionWindow), "destroy",
						GTK_SIGNAL_FUNC (QuitAppliGtk), NULL);

	PtrUIManager = InitMenusAndToolBar( vbox );
	gtk_window_add_accel_group( GTK_WINDOW( MainSectionWindow ), 
				  gtk_ui_manager_get_accel_group(PtrUIManager) );

	hboxtop = gtk_hbox_new (FALSE,0);
	gtk_container_add (GTK_CONTAINER (vbox), hboxtop);
	gtk_widget_show(hboxtop);
	gtk_box_set_child_packing(GTK_BOX(vbox), hboxtop,
		/*expand*/ FALSE, /*fill*/ FALSE, /*pad*/ 0, GTK_PACK_START);

	TooltipsEntryLabel = gtk_tooltips_new();
	entrylabel = gtk_entry_new();
	gtk_widget_set_usize((GtkWidget *)entrylabel,80,0);
	gtk_entry_set_max_length((GtkEntry *)entrylabel,LGT_LABEL-1);
	gtk_entry_prepend_text((GtkEntry *)entrylabel,"");
	gtk_box_pack_start (GTK_BOX (hboxtop), entrylabel, FALSE, FALSE, 0);
	gtk_tooltips_set_tip ( TooltipsEntryLabel, entrylabel, "Label of the current selected rung", NULL );
	gtk_widget_show(entrylabel);
	TooltipsEntryComment = gtk_tooltips_new();
	entrycomment = gtk_entry_new();
	gtk_entry_set_max_length((GtkEntry *)entrycomment,LGT_COMMENT-1);
	gtk_entry_prepend_text((GtkEntry *)entrycomment,"");
	gtk_box_pack_start (GTK_BOX (hboxtop), entrycomment, TRUE, TRUE, 0);
	gtk_tooltips_set_tip ( TooltipsEntryComment, entrycomment, "Comment of the current selected rung", NULL );
	gtk_widget_show(entrycomment);

	CheckDispSymbols = gtk_check_button_new_with_label("Display symbols");
	gtk_box_pack_start( GTK_BOX (hboxtop), CheckDispSymbols, FALSE, FALSE, 0 );
	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( CheckDispSymbols ), InfosGene->DisplaySymbols );
	gtk_signal_connect( GTK_OBJECT(CheckDispSymbols), "toggled",
				(GtkSignalFunc)CheckDispSymbols_toggled, NULL );
	gtk_widget_show( CheckDispSymbols );

	WidgetDurationOfLastScan = gtk_entry_new();
	gtk_widget_set_usize(WidgetDurationOfLastScan,80,0);
//	gtk_entry_set_max_length((GtkEntry *)WidgetDurationOfLastScan,10);
	gtk_entry_prepend_text((GtkEntry *)WidgetDurationOfLastScan,"---");
	gtk_box_pack_start (GTK_BOX (hboxtop), WidgetDurationOfLastScan, FALSE, FALSE, 0);
	gtk_widget_set_sensitive(WidgetDurationOfLastScan, FALSE);


	hboxmiddle = gtk_hbox_new (FALSE,0);
	gtk_container_add (GTK_CONTAINER (vbox), hboxmiddle);
	gtk_widget_show(hboxmiddle);
	gtk_box_set_child_packing(GTK_BOX(vbox), hboxmiddle,
		/*expand*/ TRUE, /*fill*/ TRUE, /*pad*/ 0, GTK_PACK_START);

	/* Create the drawing area */
	drawing_area = gtk_drawing_area_new ();
	gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area) ,
							BLOCK_WIDTH_DEF*RUNG_WIDTH+20 ,
							BLOCK_HEIGHT_DEF*RUNG_HEIGHT+45);
	gtk_box_pack_start (GTK_BOX (hboxmiddle), drawing_area, TRUE, TRUE, 0);
	gtk_widget_show (drawing_area);

	AdjustVScrollBar = (GtkAdjustment *)gtk_adjustment_new( 0, 0, 0, 0, 0, 0);
	VScrollBar = gtk_vscrollbar_new( AdjustVScrollBar );
	gtk_box_pack_start (GTK_BOX (hboxmiddle), VScrollBar, FALSE, FALSE, 0);
	gtk_widget_show (VScrollBar);

	AdjustHScrollBar = (GtkAdjustment *)gtk_adjustment_new( 0, 0, 0, 0, 0, 0);
	HScrollBar = gtk_hscrollbar_new( AdjustHScrollBar );
	gtk_box_pack_start (GTK_BOX (vbox), HScrollBar, FALSE, FALSE, 0);
	gtk_widget_show (HScrollBar);
	UpdateVScrollBar( TRUE/*AutoSelectCurrentRung*/ );

	gtk_signal_connect(GTK_OBJECT (AdjustVScrollBar), "value-changed",
						(GtkSignalFunc) VScrollBar_value_changed_event, 0);
	gtk_signal_connect(GTK_OBJECT (AdjustHScrollBar), "value-changed",
						(GtkSignalFunc) HScrollBar_value_changed_event, 0);

	/* Search function */
	SearchInitGtk( GTK_BOX(vbox) );
	
	FileTransferInitGtk( GTK_BOX(vbox) );

	/* Create the status bar */
	StatusBar = gtk_statusbar_new ();
//	gtk_statusbar_set_has_resize_grip( GTK_STATUSBAR(StatusBar), FALSE );
	gtk_box_pack_start (GTK_BOX(vbox), StatusBar, FALSE, FALSE, 0);
	gtk_widget_show (StatusBar);
	StatusBarContextId = gtk_statusbar_get_context_id( GTK_STATUSBAR(StatusBar), "Statusbar" );


	/* Signal used to redraw */
	gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
						(GtkSignalFunc) expose_event, NULL);
//Cairo	gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
//Cairo						(GtkSignalFunc) configure_event, NULL);

	/* Event signals */
	gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
						(GtkSignalFunc) button_press_event, NULL);
	gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
						(GtkSignalFunc) motion_notify_event, NULL);
	gtk_signal_connect (GTK_OBJECT (drawing_area), "button_release_event",
						(GtkSignalFunc) button_release_event, NULL);

	gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
							| GDK_LEAVE_NOTIFY_MASK
							| GDK_BUTTON_PRESS_MASK
							| GDK_BUTTON_RELEASE_MASK
							| GDK_POINTER_MOTION_MASK
							| GDK_POINTER_MOTION_HINT_MASK);

	gtk_signal_connect( GTK_OBJECT(MainSectionWindow), "delete_event",
		(GtkSignalFunc)MainSectionWindowDeleteEvent, 0 );
		
	gtk_window_set_icon_from_file( GTK_WINDOW(MainSectionWindow), CL_ICON_FILE, NULL );
	
	gtk_widget_show (MainSectionWindow);

	GetTheSizesForRung();
}

void RedrawSignalDrawingArea( void )
{
	GdkRegion * region = gdk_drawable_get_clip_region( drawing_area->window );
	// redraw completely by exposing it
	gdk_window_invalidate_region( drawing_area->window, region, TRUE );
	gdk_window_process_updates( drawing_area->window, TRUE );
	gdk_region_destroy( region );
}

static gint PeriodicUpdateDisplay(gpointer data)
{
gdk_threads_enter();
	if (InfosGene->LadderState==STATE_RUN)
	{
		if (InfosGene->CmdRefreshVarsBits)
		{
			RefreshAllBoolsVars();
			InfosGene->CmdRefreshVarsBits = FALSE;
		}
		DisplayFreeVarSpy();
#ifdef COMPLETE_PLC
		if ( InfosGene->LogContentModified )
		{
			DisplayLogBookEvents( TRUE/*OnLogContentModified*/ );
			InfosGene->LogContentModified = FALSE;
		}
		if ( InfosGene->DefaultLogListModified )
		{
			int NbrDefs = FindCurrentDefaults( );
			InfosGene->DefaultLogListModified = FALSE;
			if ( NbrDefs>0 )
			{
				char * ListDefaultsText = (char *)malloc( NbrDefs*(EVENT_SYMBOL_LGT+10)+10 );
				if ( ListDefaultsText )
				{
					StrConfigEventLog * pCfgEvtLog;
					int ScanList;
					char OneEventText[ EVENT_SYMBOL_LGT+10 ];
					sprintf( ListDefaultsText, "DEFAULT%s : ", NbrDefs>1?"S":"");
printf("nbr defaults=%d\n", NbrDefs);
					for( ScanList=0; ScanList<NbrDefs; ScanList++ )
					{
						pCfgEvtLog = &ConfigEventLog[ ListCurrentDefType[ ScanList ] ];
						//display value parameter after symbol name if many variables configured for the same event !
						if ( ListCurrentDefParam[ ScanList ]!=-1 )
							sprintf( OneEventText, "%s%d ", pCfgEvtLog->Symbol, ListCurrentDefParam[ ScanList ] );
						else
							sprintf( OneEventText, "%s ", pCfgEvtLog->Symbol );
						strcat( ListDefaultsText, OneEventText );
					}
					MessageInStatusBar( ListDefaultsText );
					free( ListDefaultsText );
				}
			}
			else
			{
				MessageInStatusBar( "No default." );
			}
		}
#endif
	}
	if (InfosGene->LadderState!=STATE_LOADING )
	{
//		DrawCurrentSection( );
//		CairoDrawCurrentSection( );
		RedrawSignalDrawingArea( );
	}
	if ( InfosGene->HardwareErrMsgToDisplay[ 0 ]!='\0' )
	{
		ShowMessageBox( "Config hardware error occured!", InfosGene->HardwareErrMsgToDisplay, "Ok" );
		InfosGene->HardwareErrMsgToDisplay[ 0 ] = '\0';
	}

	if( InfosGUI->TargetMonitor.RemoteConnected )
	{
		RefreshAllBoolsVars();
		DisplayFreeVarSpy();
		RedrawSignalDrawingArea( );
		if ( InfosGUI->TargetMonitor.CmdDisplayTargetInfosVersion )
		{
			DisplayTargetInfosVersion( );
			InfosGUI->TargetMonitor.CmdDisplayTargetInfosVersion = FALSE;
		}
	}
	if ( InfosGene->DurationOfLastScan!=-1 )
	{
		char TextBuffer[ 20 ];
		sprintf(TextBuffer , "%d us", InfosGene->DurationOfLastScan/1000);
		gtk_entry_set_text(GTK_ENTRY(WidgetDurationOfLastScan),TextBuffer);
		gtk_widget_show( WidgetDurationOfLastScan );
	}
	else
	{
		gtk_widget_hide( WidgetDurationOfLastScan );
	}
gdk_threads_leave();
	return 1;
}


void InitGtkWindows( int argc, char *argv[] )
{
	SearchDatas.ElementOrSearchType = ELE_FREE;

	debug_printf( "Your GTK+ version is: %d.%d.%d and zlib version is: %s\n", gtk_major_version, gtk_minor_version,
			gtk_micro_version, ZLIB_VERSION );
//ProblemWithPrint	g_thread_init (NULL);
//ProblemWithPrint	gdk_threads_init ();
//set back for monitor window...
	g_thread_init (NULL);
	gdk_threads_init( );
	gdk_threads_enter();
	gtk_init (&argc, &argv);

	VarsWindowInitGtk();
	MainSectionWindowInitGtk();
//moved before, else crashing when adding tooltips...?
//	VarsWindowInitGtk();
	EditorInitGtk();
	PropertiesInitGtk();
	ManagerInitGtk( );
	SymbolsInitGtk( );
#ifdef COMPLETE_PLC
	LogBookInitGtk( );
#endif
	MonitorWindowInitGtk( );
	// restore each window open/closed state at startup !
	if ( GetWindowOpenPrefs( "Edit" ) )
		SetToggleMenuForEditorWindow( TRUE );
	if ( GetWindowOpenPrefs( "Symbols" ) )
		SetToggleMenuForSymbolsWindow( TRUE );
	if ( GetWindowOpenPrefs( "BoolVars" ) )
		SetToggleMenuForBoolVarsWindow( TRUE );
	if ( GetWindowOpenPrefs( "FreeVars" ) )
		SetToggleMenuForFreeVarsWindow( TRUE );
	if ( GetWindowOpenPrefs( "LogBook" ) )
		SetToggleMenuForLogWindow( TRUE );
	if ( GetWindowOpenPrefs( "Monitor" ) )
		SetToggleMenuForMonitorWindow( TRUE );

//->moved in classicladder.c SetMenuStateForRunStopSwitch( TRUE );
#ifdef COMPLETE_PLC
SetGtkMenuStateForConnectDisconnectSwitch( FALSE );
#endif
//deprecated	gtk_timeout_add( TIME_UPDATE_GTK_DISPLAY_MS, PeriodicUpdateDisplay, NULL );
	g_timeout_add( TIME_UPDATE_GTK_DISPLAY_MS, PeriodicUpdateDisplay, NULL );
}

void UpdateAllGtkWindows( void )
{
//	DrawCurrentSection( );
	RedrawSignalDrawingArea( );
	refresh_label_comment( );
	autorize_prevnext_buttons( TRUE );
	UpdateVScrollBar( TRUE/*AutoSelectCurrentRung*/ );
	ManagerDisplaySections( );
	DisplaySymbols( );
	UpdateAllLabelsBoolsVars( -1/*OnlyThisColumn*/ );
	UpdateAllLabelsFreeVars( -1/*OnlyThisOne*/, NULL );
	DisplayProjectProperties( );
}

void UpdateWindowTitleWithProjectName( void )
{
	char Buff[ 250 ];
	int ScanFileNameOnly = 0;
	int LgtProjectFileName = strlen(InfosGene->CurrentProjectFileName);
	if ( LgtProjectFileName==0 )
	{
		sprintf( Buff, "ClassicLadder Section Display (No project)%s",
			InfosGUI->TargetMonitor.RemoteConnected?" - CONNECTED":"\0" );
	}
	else 
	{
		if ( LgtProjectFileName>2 )
		{
			ScanFileNameOnly = LgtProjectFileName-1;
			while( ScanFileNameOnly>0 && InfosGene->CurrentProjectFileName[ScanFileNameOnly-1]!='/' && InfosGene->CurrentProjectFileName[ScanFileNameOnly-1]!='\\')
				ScanFileNameOnly--;
		}
		sprintf( Buff, "ClassicLadder Section Display (%s)%s", &InfosGene->CurrentProjectFileName[ScanFileNameOnly],
			InfosGUI->TargetMonitor.RemoteConnected?" - CONNECTED":"\0" );
	}
	gtk_window_set_title( GTK_WINDOW(MainSectionWindow), Buff );
}
